using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

//**********************************************
// Base Primitive class, template for primitives
//**********************************************
namespace MechaShark
{
    public abstract class BasePrimitive : IDisposable  //IDisposable means we create our own destructor
    {
        //-------------Members-------------
        List<VertexPositionNormal> vertices = new List<VertexPositionNormal>(); //list of vertices
        List<ushort> indices = new List<ushort>(); //ushort is unsigned int16

        VertexBuffer vertexBuffer;
        IndexBuffer indexBuffer;
        BasicEffect currentEffect;
        //-------------Methods-------------
        #region Initialization
        //Vertex appender
        protected void AddVertex(Vector3 position, Vector3 normal)
        {
            vertices.Add(new VertexPositionNormal(position, normal)); //adds new Vertices to our vertices list
        }
        //Index Incrementor
        protected void AddIndex(int index)
        {
            if (index > ushort.MaxValue) //exception for when our ushort index is too high
                throw new ArgumentOutOfRangeException("index");
            indices.Add((ushort)index); //add index, casted as ushort
        }

        //Returns current index
        protected int CurrentVertex
        {
            get { return vertices.Count; } //returns our vertices list cound, necessary because it is a private
        }

        //Initializer
        protected void InitializePrimitive(GraphicsDevice Device)
        {
            //copy vertex data into vertexBuffer
            vertexBuffer = new VertexBuffer(Device,
                                            typeof(VertexPositionNormal),
                                            vertices.Count, BufferUsage.None); //init vertexBuffer
            vertexBuffer.SetData(vertices.ToArray()); //inputs vertices into vertexBuffer

            //copy index data into indexBuffer
            indexBuffer = new IndexBuffer(Device, typeof(ushort), indices.Count, BufferUsage.None); //init indexBuffer
            indexBuffer.SetData(indices.ToArray()); //copy indices into index buffer

            //our basic effect, for rendering
            currentEffect = new BasicEffect(Device);
            currentEffect.EnableDefaultLighting(); //includes lighting
        }
        #region Deallocation
        ~BasePrimitive()
        {
            Dispose(false);
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (vertexBuffer != null)
                    vertexBuffer.Dispose();
                if (indexBuffer != null)
                    indexBuffer.Dispose();
                if (currentEffect != null)
                    currentEffect.Dispose();
            }
        }
        #endregion
        #endregion
        #region Drawing
        //overloaded draw
        public void Draw(BasicEffect effect) //internal draw function
        {
            GraphicsDevice graphicsDevice = effect.GraphicsDevice;

            // Set our vertex declaration, vertex buffer, and index buffer.
            graphicsDevice.SetVertexBuffer(vertexBuffer);

            graphicsDevice.Indices = indexBuffer;


            foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
            {
                effectPass.Apply();

                int primitiveCount = indices.Count / 3;

                graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
                                                     vertices.Count, 0, primitiveCount);

            }
        }
        public void Draw(Matrix view, Matrix proj, Color color) //insert world later
        {
            currentEffect.View = view;
            currentEffect.Projection = proj;
            currentEffect.DiffuseColor = color.ToVector3();
            currentEffect.Alpha = color.A / 255.0f; //sets transparency

            GraphicsDevice device = currentEffect.GraphicsDevice;
            device.DepthStencilState = DepthStencilState.Default; //sets z buffer to default

            if (color.A < 255)
            {
                //alpha blended
                device.BlendState = BlendState.AlphaBlend;
            }
            else
            {
                //opaque rendering
                device.BlendState = BlendState.Opaque;
            }

            Draw(currentEffect);
        }
        #endregion

    }
}
